home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / unix / mnx12hd / xt_wini.c < prev   
Encoding:
C/C++ Source or Header  |  1988-01-29  |  26.5 KB  |  848 lines

  1. /* This file contains a driver for the WD winchester controller from 
  2.  * Western Digital (WX-2 and related controllers).
  3.  *
  4.  * Original code written by Adri Koppes.
  5.  * Patches from Gary Oliver for use with the Western Digital WX-2.
  6.  * Patches from Harry McGavran for robust operation on turbo clones.
  7.  * Patches from Mike Mitchell for WX-2 auto configure operation.
  8.  *
  9.  * The driver supports two operations: read a block and
  10.  * write a block.  It accepts two messages, one for reading and one for
  11.  * writing, both using message format m2 and with the same parameters:
  12.  *
  13.  *    m_type      DEVICE   PROC_NR    COUNT     POSITION  ADRRESS
  14.  * ----------------------------------------------------------------
  15.  * |  DISK_READ | device  | proc nr |  bytes  |     offset | buf ptr |
  16.  * |------------+---------+---------+---------+---------+---------|
  17.  * | DISK_WRITE | device  | proc nr |  bytes  |     offset | buf ptr |
  18.  * ----------------------------------------------------------------
  19.  *
  20.  * The file contains one entry point:
  21.  *
  22.  *     winchester_task:    main entry when system is brought up
  23.  *
  24.  */
  25.  
  26. #include "../h/const.h"
  27. #include "../h/type.h"
  28. #include "../h/callnr.h"
  29. #include "../h/com.h"
  30. #include "../h/error.h"
  31. #include "const.h"
  32. #include "type.h"
  33. #include "proc.h"
  34.  
  35. #define AU  TO_BIOS     TRUE    /* TRUE: use Western's autoconfig BIOS */
  36. #define DEBUG           FALSE    /* TRUE: enable debug messages */
  37. #define MONITOR        TRUE    /* TRUE: monitor performance of busy loops */
  38. #define MAX_DRIVES         1
  39.  
  40. /* I/O Ports used by winchester disk task. */
  41. #define WIN_DATA       0x320    /* winchester disk controller data register */
  42. #define WIN_STATUS     0x321    /* winchester disk controller status register */
  43. #define WST_REQ           0x001    /* Request bit */
  44. #define WST_INPUT      0x002    /* Set if controller is writing to cpu */
  45. #define WST_BUS           0x004    /* Command/status bit */
  46. #define WST_BUSY       0x008    /* Busy */
  47. #define WST_INTERRUPT  0x020    /* Interrupt generated ?? */
  48. #define WIN_SELECT     0x322    /* winchester disk controller select port */
  49. #define WIN_DMA           0x323    /* winchester disk controller dma register */
  50. #define DMA_ADDR       0x006    /* port for low 16 bits of DMA address */
  51. #define DMA_TOP           0x082    /* port for top 4 bits of 20-bit DMA addr */
  52. #define DMA_COUNT      0x007    /* port for DMA count (count =    bytes - 1) */
  53. #define DMA_M2           0x00C    /* DMA status port */
  54. #define DMA_M1           0x00B    /* DMA status port */
  55. #define DMA_INIT       0x00A    /* DMA init port */
  56.  
  57. /* Winchester disk controller command bytes. */
  58. #define WIN_RECALIBRATE    0x01    /* command for the drive to recalibrate */
  59. #define WIN_SENSE    0x03    /* command for the controller to get its status */
  60. #define WIN_READ    0x08    /* command for the drive to read */
  61. #define WIN_WRITE    0x0a    /* command for the drive to write */
  62. #define WIN_SPECIFY    0x0C    /* command for the controller to accept params    */
  63. #define WIN_ECC_READ    0x0D    /* command for the controller to read ecc length */
  64.  
  65. #define DMA_INT           3 /* Command with dma and interrupt */
  66. #define INT           2    /* Command with interrupt, no dma */
  67. #define NO_DMA_INT       0    /* Command without dma and interrupt */
  68.  
  69. /* DMA channel commands. */
  70. #define DMA_READ    0x47    /* DMA read opcode */
  71. #define DMA_WRITE    0x4B    /* DMA write opcode */
  72.  
  73. /* Parameters for the disk drive. */
  74. #define SECTOR_SIZE     512    /* physical sector size in bytes */
  75. #define NR_SECTORS    0x11    /* number of sectors per track */
  76.  
  77. /* Error codes */
  78. #define ERR          -1    /* general error */
  79.  
  80. /* Miscellaneous. */
  81. #define MAX_ERRORS       4    /* how often to try rd/wt before quitting */
  82. #define MAX_RESULTS       4    /* max number of bytes controller returns */
  83. #define NR_DEVICES      10    /* maximum number of drives */
  84. #define MAX_WIN_RETRY  32000    /* max # times to try to output to WIN */
  85. #define PART_TABLE     0x1C6    /* IBM partition table starts here in sect 0 */
  86. #define DEV_PER_DRIVE       5    /* hd0 + hd1 + hd2 + hd3 + hd4 = 5 */
  87. #if AUTO_BIOS
  88. #define AUTO_PARAM     0x1AD    /* drive parameter table starts here in sect 0    */
  89. #define AUTO_ENABLE    0x10    /* auto bios enabled bit from status reg */
  90. /* some start up parameters in order to extract the drive parameter table */
  91. /* from the winchester. these should not need changed. */
  92. #define AUTO_CYLS     306    /* default number of cylinders */
  93. #define AUTO_HEADS       4    /* default number of heads */
  94. #define AUTO_RWC     307    /* default reduced write cylinder */
  95. #define AUTO_WPC     307    /* default write precomp cylinder */
  96. #define AUTO_ECC      11    /* default ecc burst */
  97. #define AUTO_CTRL       5    /* default winchester stepping speed byte */
  98. #endif
  99.  
  100. /* Variables. */
  101. PRIVATE struct wini {        /* main drive struct, one entry per drive */
  102.   int wn_opcode;        /* DISK_READ or DISK_WRITE */
  103.   int wn_procnr;        /* which proc wanted this operation? */
  104.   int wn_drive;            /* drive number addressed (<< 5) */
  105.   int wn_cylinder;        /* cylinder number addressed */
  106.   int wn_sector;        /* sector addressed */
  107.   int wn_head;            /* head number addressed */
  108.   int wn_heads;            /* maximum number of heads */
  109.   int wn_ctrl_byte;        /* Control byte for COMMANDS (10-Apr-87 GO) */
  110.   long wn_low;            /* lowest cylinder of partition */
  111.   long wn_size;            /* size of partition in blocks */
  112.   int wn_count;            /* byte count */
  113.   vir_bytes wn_address;        /* user virtual address */
  114.   char wn_results[MAX_RESULTS];    /* the controller can give lots of output */
  115. } wini[NR_DEVICES];
  116.  
  117. PRIVATE int w_need_reset = FALSE;     /* set to 1 when controller must be reset    */
  118. PRIVATE int nr_drives;         /* Number of drives */
  119.  
  120. PRIVATE message w_mess;        /* message buffer for in and out */
  121.  
  122. PRIVATE int command[6];        /* Common command block */
  123.  
  124. PRIVATE unsigned char buf[BLOCK_SIZE]; /* Buffer used by the startup routine */
  125.  
  126. PRIVATE struct param {
  127.     int nr_cyl;        /* Number of cylinders */
  128.     int nr_heads;        /* Number of heads */
  129.     int reduced_wr;        /* First cylinder with reduced write current */
  130.     int wr_precomp;        /* First cylinder with write precompensation */
  131.     int max_ecc;        /* Maximum ECC burst length */
  132.     int ctrl_byte;        /* Copied control-byte from bios tables */
  133. } param0, param1;
  134.  
  135.  
  136. /*=========================================================================*
  137.  *                            winchester_task                                    * 
  138.  *=========================================================================*/
  139. PUBLIC winchester_task()
  140. {
  141. /* Main program of the winchester disk driver task. */
  142.  
  143.   int r, caller, proc_nr;
  144.  
  145.   /* First initialize the controller */
  146.   init_params();
  147.  
  148.   /* Here is the main loop of the disk task.  It waits for a message, carries
  149.    * it out, and sends a reply.
  150.    */
  151.  
  152.   while (TRUE) {
  153.     /* First wait for a request to read or write a disk block. */
  154.     receive(ANY, &w_mess);    /* get a request to do some work */
  155.     if (w_mess.m_source < 0) {
  156.         printf("winchester task got message from %d ", w_mess.m_source);
  157.         continue;
  158.     }
  159.     caller = w_mess.m_source;
  160.     proc_nr = w_mess.PROC_NR;
  161.  
  162.     /* Now carry out the work. */
  163.     switch(w_mess.m_type) {
  164.         case DISK_READ:
  165.         case DISK_WRITE:    r = w_do_rdwt(&w_mess);    break;
  166.         default:        r = EINVAL;        break;
  167.     }
  168.  
  169.     /* Finally, prepare and send the reply message. */
  170.     w_mess.m_type = TASK_REPLY;    
  171.     w_mess.REP_PROC_NR = proc_nr;
  172.  
  173.     w_mess.REP_STATUS = r;    /* # of bytes transferred or error code */
  174.     send(caller, &w_mess);    /* send reply to caller */
  175.   }
  176. }
  177.  
  178.  
  179. /*==========================================================================*
  180.  *                                w_do_rdwt                                     * 
  181.  *==========================================================================*/
  182. PRIVATE int w_do_rdwt(m_ptr)
  183. message *m_ptr;            /* pointer to read or write w_message */
  184. {
  185. /* Carry out a read or write request from the disk. */
  186.   register struct wini *wn;
  187.   int r, device, errors = 0;
  188.   long sector;
  189.  
  190.   /* Decode the w_message parameters. */
  191.   device = m_ptr->DEVICE;
  192.   if (device < 0 || device >= NR_DEVICES)
  193.     return(EIO);
  194.   if (m_ptr->COUNT != BLOCK_SIZE)
  195.     return(EINVAL);
  196.   wn = &wini[device];        /* 'wn' points to entry for this drive */
  197.  
  198.   wn->wn_opcode = m_ptr->m_type;    /* DISK_READ or DISK_WRITE */
  199.   if (m_ptr->POSITION % BLOCK_SIZE != 0)
  200.     return(EINVAL);
  201.   sector = m_ptr->POSITION/SECTOR_SIZE;
  202.   if ((sector+BLOCK_SIZE/SECTOR_SIZE) > wn->wn_size)
  203.     return(EOF);
  204.   sector += wn->wn_low;
  205.   wn->wn_cylinder = sector / (wn->wn_heads * NR_SECTORS);
  206.   wn->wn_sector =  (sector % NR_SECTORS);
  207.   wn->wn_head = (sector % (wn->wn_heads * NR_SECTORS) )/NR_SECTORS;
  208.   wn->wn_count = m_ptr->COUNT;
  209.   wn->wn_address = (vir_bytes) m_ptr->ADDRESS;
  210.   wn->wn_procnr = m_ptr->PROC_NR;
  211.  
  212.   /* This loop allows a failed operation to be repeated. */
  213.   while (errors <= MAX_ERRORS) {
  214.     errors++;        /* increment count once per loop cycle */
  215.     if (errors >= MAX_ERRORS)
  216.         return(EIO);
  217.  
  218.     /* First check to see if a reset is needed. */
  219.     if (w_need_reset) w_reset();
  220.  
  221.     /* Now set up the DMA chip. */
  222.     w_dma_setup(wn);
  223.  
  224.     /* Perform the transfer. */
  225.     r = w_transfer(wn);
  226.     if (r == OK) break;    /* if successful, exit loop */
  227.  
  228.   }
  229.  
  230.   return(r == OK ? BLOCK_SIZE : EIO);
  231. }
  232.  
  233.  
  234. /*==========================================================================*
  235.  *                                w_dma_setup                                     * 
  236.  *==========================================================================*/
  237. PRIVATE w_dma_setup(wn)
  238. struct wini *wn;        /* pointer to the drive struct */
  239. {
  240. /* The IBM PC can perform DMA operations by using the DMA chip.     To use it,
  241.  * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
  242.  * to by read from or written to, the byte count minus 1, and a read or write
  243.  * opcode.    This routine sets up the DMA chip.    Note that the chip is not
  244.  * capable of doing a DMA across a 64K boundary (e.g., you can't read a 
  245.  * 512-byte block starting at physical address 65520).
  246.  */
  247.  
  248.   int mode, low_addr, high_addr, top_addr, low_ct, high_ct, top_end;
  249.   vir_bytes vir, ct;
  250.   phys_bytes user_phys;
  251.   extern phys_bytes umap();
  252.  
  253.   mode = (wn->wn_opcode == DISK_READ ? DMA_READ : DMA_WRITE);
  254.   vir = (vir_bytes) wn->wn_address;
  255.   ct = (vir_bytes) wn->wn_count;
  256.   user_phys = umap(proc_addr(wn->wn_procnr), D, vir, ct);
  257.   low_addr    = (int) user_phys & BYTE;
  258.   high_addr = (int) (user_phys >>  8) & BYTE;
  259.   top_addr    = (int) (user_phys >> 16) & BYTE;
  260.   low_ct  = (int) (ct - 1) & BYTE;
  261.   high_ct = (int) ( (ct - 1) >> 8) & BYTE;
  262.  
  263.   /* Check to see if the transfer will require the DMA address counter to
  264.    * go from one 64K segment to another.  If so, do not even start it, since
  265.    * the hardware does not carry from bit 15 to bit 16 of the DMA address.
  266.    * Also check for bad buffer address.     These errors mean FS contains a bug.
  267.    */
  268.   if (user_phys == 0)
  269.       panic("FS gave winchester disk driver bad addr", (int) vir);
  270.   top_end = (int) (((user_phys + ct - 1) >> 16) & BYTE);
  271.   if (top_end != top_addr) panic("Trying to DMA across 64K boundary", top_addr);
  272.  
  273.   /* Now set up the DMA registers. */
  274.   lock();
  275.   port_out(DMA_M2, mode);    /* set the DMA mode */
  276.   port_out(DMA_M1, mode);    /* set it again */
  277.   port_out(DMA_ADDR, low_addr);    /* output low-order 8 bits */
  278.   port_out(DMA_ADDR, high_addr);/* output next 8 bits */
  279.   port_out(DMA_TOP, top_addr);    /* output highest 4 bits */
  280.   port_out(DMA_COUNT, low_ct);    /* output low 8 bits of count - 1 */
  281.   port_out(DMA_COUNT, high_ct);    /* output high 8 bits of count - 1 */
  282.   unlock();
  283. }
  284.  
  285. /*=========================================================================*
  286.  *                                w_transfer                                   *
  287.  *=========================================================================*/
  288. PRIVATE int w_transfer(wn)
  289. register struct wini *wn;    /* pointer to the drive struct */
  290. {
  291. /* The drive is now on the proper cylinder.     Read or write 1 block. */
  292.  
  293.   /* The command is issued by outputing 6 bytes to the controller chip. */
  294.   command[0] = (wn->wn_opcode == DISK_READ ? WIN_READ : WIN_WRITE);
  295.   command[1] = wn->wn_head | wn->wn_drive;
  296.   command[2] = (((wn->wn_cylinder & 0x0300) >> 2) | wn->wn_sector);
  297.   command[3] = (wn->wn_cylinder & 0xFF);
  298.   command[4] = BLOCK_SIZE/SECTOR_SIZE;
  299.   command[5] = wn->wn_ctrl_byte;
  300.  
  301.   if (com_out(DMA_INT) != OK)
  302.     return(ERR);
  303.  
  304.   port_out(DMA_INIT, 3);    /* initialize DMA */
  305.   /* Block, waiting for disk interrupt. */
  306.   w_wait_int();
  307.  
  308.   /* Get controller status and check for errors. */
  309.   if (win_results(wn) == OK)
  310.     return(OK);
  311.   if ((wn->wn_results[0] & 63) == 24)
  312.     read_ecc();
  313.   else
  314.     w_need_reset = TRUE;
  315.   return(ERR);
  316. }
  317.  
  318.  
  319. /*===========================================================================*
  320.  *                win_results                     * 
  321.  *===========================================================================*/
  322. PRIVATE int win_results(wn)
  323. register struct wini *wn;    /* pointer to the drive struct */
  324. {
  325. /* Extract results from the controller after an operation. */
  326.  
  327.   register int i;
  328.   int status;
  329.  
  330.   port_in(WIN_DATA, &status);
  331.   port_out(WIN_DMA, 0);
  332.   if (!(status & 2))        /* Test "error" bit */
  333.     return(OK);
  334.   command[0] = WIN_SENSE;
  335.   command[1] = wn->wn_drive;
  336.   if (com_out(NO_DMA_INT) != OK)
  337.     return(ERR);
  338.  
  339.   /* Loop, extracting bytes from WIN */
  340.   for (i = 0; i < MAX_RESULTS; i++) {
  341.     if (hd_wait(WST_REQ) != OK)
  342.         return(ERR);
  343.     port_in(WIN_DATA, &status);
  344.     wn->wn_results[i] = status & BYTE;
  345.   }
  346.   if(hd_wait(WST_REQ) != OK)    /* Missing from            */
  347.      return (ERR);        /* Original.  11-Apr-87 G.O.    */
  348.  
  349.   port_in(WIN_DATA, &status);         /* Read "error" flag */
  350.  
  351.   if(((status & 2) != 0) || (wn->wn_results[0] & 0x3F)) {
  352.     return(ERR);
  353.   } else
  354.     return(OK);
  355. }
  356.  
  357.  
  358. /*===========================================================================*
  359.  *                win_out                         * 
  360.  *===========================================================================*/
  361. PRIVATE win_out(val)
  362. int val;            /* write this byte to winchester disk controller */
  363. {
  364. /* Output a byte to the controller.     This is not entirely trivial, since you
  365.  * can only write to it when it is listening, and it decides when to listen.
  366.  * If the controller refuses to listen, the WIN chip is given a hard reset.
  367.  */
  368.   int r;
  369.  
  370.   if (w_need_reset) return;    /* if controller is not listening, return */
  371.  
  372.   do {
  373.     port_in(WIN_STATUS, &r);
  374.   } while((r & (WST_REQ | WST_BUSY)) == WST_BUSY);
  375.  
  376.     port_out(WIN_DATA, val);
  377. }
  378.  
  379. /*===========================================================================*
  380.  *                w_reset                         * 
  381.  *===========================================================================*/
  382. PRIVATE w_reset()
  383. {
  384. /* Issue a reset to the controller.     This is done after any catastrophe,
  385.  * like the controller refusing to respond.
  386.  */
  387.  
  388.   int r = 0, i;
  389.  
  390.   /* Strobe reset bit low. */
  391.   port_out(WIN_STATUS, 0);
  392.  
  393.   for(i = MAX_WIN_RETRY/10; i; --i)
  394.     ;    /* Spin loop for a while */
  395.  
  396.   port_out(WIN_SELECT, 0);    /* Issue select pulse */
  397.   for (i = 0; i < MAX_WIN_RETRY; i++) {
  398.     port_in(WIN_STATUS, &r);
  399.     if(r & 0x30)        /* What is 10? 20 = INTERRUPT */
  400.         return (ERR);
  401.  
  402.     if((r & (WST_BUSY | WST_BUS | WST_REQ)) ==
  403.         (WST_BUSY | WST_BUS | WST_REQ))
  404.         break;
  405.   }
  406.  
  407.   if (i == MAX_WIN_RETRY) {
  408.     printf("Hard disk won't reset, status = %x\n", r);
  409.     return(ERR);
  410.   }
  411.  
  412.   /* Reset succeeded.  Tell WIN drive parameters. */
  413.   w_need_reset = FALSE;
  414.  
  415.   if(win_specify(0, ¶m0) != OK)
  416.     return (ERR);
  417.  
  418.  
  419.   if ((nr_drives > 1) && (win_specify(1, ¶m1) != OK))
  420.     return(ERR);
  421.  
  422.  
  423.   for (i=0; i<nr_drives; i++) {
  424.     command[0] = WIN_RECALIBRATE;
  425.     command[1] = i << 5;
  426.     command[5] = wini[i * DEV_PER_DRIVE].wn_ctrl_byte;
  427.  
  428.  
  429.     if (com_out(INT) != OK)
  430.         return(ERR);
  431.  
  432.     w_wait_int();
  433.  
  434.     if (win_results(&wini[i * DEV_PER_DRIVE]) != OK) {
  435.         w_need_reset = TRUE;
  436.         return(ERR);
  437.     }
  438.      }
  439.      return(OK);
  440. }
  441.  
  442.  
  443. /*===========================================================================*
  444.  *                w_wait_int                     *
  445.  *===========================================================================*/
  446. PRIVATE w_wait_int()
  447. {
  448.    /*DEBUG: loop looking for 0x20 in status (I don't know what that is!!) */
  449.    /*         10-Apr-87. G. Oliver                      */
  450.    int r, i; /* Some local storage */
  451.  
  452.    receive(HARDWARE, &w_mess);
  453.  
  454.    port_out(DMA_INIT, 0x07);    /* Disable int from DMA */
  455.  
  456.    for(i=0; i<MAX_WIN_RETRY; ++i) {
  457.     port_in(WIN_STATUS, &r);
  458.     if(r & WST_INTERRUPT)
  459.         break;        /* Exit if end of int */
  460.   }
  461.  
  462. #if     MONITOR
  463.    if(i > 10) {        /* Some arbitrary limit below which we don't really care */
  464.     if(i == MAX_WIN_RETRY)
  465.         printf("wini: timeout waiting for INTERRUPT status\n");
  466.     else
  467.         printf("wini: %d loops waiting for INTERRUPT status\n", i);
  468.    }
  469. #endif    /* MONITOR */
  470. }
  471.  
  472.  
  473. /*============================================================================*
  474.  *                win_specify                      *
  475.  *============================================================================*/
  476. PRIVATE win_specify(drive, paramp)
  477. int drive;
  478. struct param *paramp;
  479. {
  480.   command[0] = WIN_SPECIFY;        /* Specify some parameters */
  481.   command[1] = drive << 5;        /* Drive number */
  482.  
  483.     if (com_out(NO_DMA_INT) != OK)        /* Output command block */
  484.         return(ERR);
  485.     lock();
  486.  
  487.     /* No. of cylinders (high byte) */
  488.   win_out(paramp->nr_cyl >> 8);
  489.  
  490.     /* No. of cylinders (low byte) */
  491.   win_out(paramp->nr_cyl);
  492.  
  493.     /* No. of heads */
  494.   win_out(paramp->nr_heads);
  495.  
  496.     /* Start reduced write (high byte) */
  497.   win_out(paramp->reduced_wr >> 8);
  498.  
  499.     /* Start reduced write (low byte) */
  500.   win_out(paramp->reduced_wr);
  501.  
  502.     /* Start write precompensation (high byte) */
  503.   win_out(paramp->wr_precomp >> 8);
  504.  
  505.     /* Start write precompensation (low byte) */
  506.   win_out(paramp->wr_precomp);
  507.  
  508.     /* Ecc burst length */
  509.   win_out(paramp->max_ecc);
  510.     unlock();
  511.  
  512.     if (check_init() != OK) {  /* See if controller accepted parameters */
  513.         w_need_reset = TRUE;
  514.         return(ERR);
  515.     }
  516.   else
  517.   return(OK);
  518. }
  519.  
  520. /*============================================================================*
  521.  *                check_init                      *
  522.  *============================================================================*/
  523. PRIVATE check_init()
  524. {
  525. /* Routine to check if controller accepted the parameters */
  526.   int r, s;
  527.  
  528.   if (hd_wait(WST_REQ | WST_INPUT) == OK) {
  529.       port_in(WIN_DATA, &r);
  530.  
  531.        do {
  532.         port_in(WIN_STATUS, &s);
  533.        } while(s & WST_BUSY);        /* Loop while still busy */
  534.  
  535.        if (r & 2)        /* Test error bit */
  536.         {
  537.         return(ERR);
  538.         }
  539.       else
  540.         return(OK);
  541.   } else
  542.     {
  543.     return (ERR);    /* Missing from original: 11-Apr-87 G.O. */
  544.   }
  545. }
  546.  
  547. /*============================================================================*
  548.  *                read_ecc                      *
  549.  *============================================================================*/
  550. PRIVATE read_ecc()
  551. {
  552. /* Read the ecc burst-length and let the controller correct the data */
  553.  
  554.   int r;
  555.  
  556.   command[0] = WIN_ECC_READ;
  557.   if (com_out(NO_DMA_INT) == OK && hd_wait(WST_REQ) == OK) {
  558.     port_in(WIN_DATA, &r);
  559.     if (hd_wait(WST_REQ) == OK) {
  560.         port_in(WIN_DATA, &r);
  561.         if (r & 1)
  562.             w_need_reset = TRUE;
  563.     }
  564.   }
  565.   return(ERR);
  566. }
  567.  
  568. /*============================================================================*
  569.  *                hd_wait                          *
  570.  *============================================================================*/
  571. PRIVATE hd_wait(bits)
  572. register int bits;
  573. {
  574. /* Wait until the controller is ready to receive a command or send status */
  575.  
  576.   register int i = 0;
  577.   int r;
  578.  
  579.   do {
  580.     port_in(WIN_STATUS, &r);
  581.     r &= bits;
  582.   } while ((i++ < MAX_WIN_RETRY) && r != bits);        /* Wait for ALL bits */
  583.  
  584.   if (i >= MAX_WIN_RETRY) {
  585.     w_need_reset = TRUE;
  586.     return(ERR);
  587.   } else
  588.     return(OK);
  589. }
  590.  
  591. /*============================================================================*
  592.  *                com_out                          *
  593.  *============================================================================*/
  594. PRIVATE com_out(mode)
  595. int mode;
  596. {
  597. /* Output the command block to the winchester controller and return status */
  598.  
  599.     register int i;
  600.     int r;
  601.  
  602.     port_out(WIN_DMA, mode);
  603.     port_out(WIN_SELECT, mode);
  604.     for (i=0; i<MAX_WIN_RETRY; i++) {
  605.         port_in(WIN_STATUS, &r);
  606.         if (r & WST_BUSY)
  607.             break;
  608.     }
  609.  
  610.     if (i == MAX_WIN_RETRY) {
  611.         w_need_reset = TRUE;
  612.         return(ERR);
  613.     }
  614.  
  615.  
  616.     lock();
  617.  
  618.     for (i=0; i<6; i++) {
  619.         if(hd_wait(WST_REQ) != OK)
  620.             break;        /* No data request pending */
  621.  
  622.         port_in(WIN_STATUS, &r);
  623.  
  624.         if((r & (WST_BUSY | WST_BUS | WST_INPUT)) !=
  625.             (WST_BUSY | WST_BUS))
  626.             break;
  627.  
  628.         port_out(WIN_DATA, command[i]);
  629.     }
  630.  
  631.     unlock();
  632.  
  633.     if(i != 6) {
  634.         return(ERR);
  635.     }
  636.     else
  637.         return(OK);
  638. }
  639.  
  640. /*============================================================================*
  641.  *                init_params                      *
  642.  *============================================================================*/
  643. PRIVATE init_params()
  644. {
  645. /* This routine is called at startup to initialize the partition table,
  646.  * the number of drives and the controller
  647. */
  648.   unsigned int i, segment, offset;
  649.   int type_0, type_1;
  650.   phys_bytes address;
  651.   extern phys_bytes umap();
  652.   extern int vec_table[];
  653.  
  654.   /* Get the number of drives from the bios */
  655.   phys_copy(0x475L, umap(proc_addr(WINCHESTER), D, buf, 1), 1L);
  656.   nr_drives = (int) *buf > MAX_DRIVES ? MAX_DRIVES : (int) *buf;
  657.  
  658.   /* Read the switches from the controller */
  659.   port_in(WIN_SELECT, &i);
  660.  
  661. #if AUTO_BIOS
  662.   /* Get the drive parameters from sector zero of the drive if the */
  663.   /* autoconfig mode of the controller has been selected */
  664.  
  665.   if(i & AUTO_ENABLE) {
  666.  
  667.     /* set up some phoney parameters so that we can read the first sector */
  668.     /* from the winchester. all drives will have one cylinder and one head */
  669.     /* but set up initially to the mini scribe drives from ibm */
  670.     param1.nr_cyl = param0.nr_cyl = AUTO_CYLS;
  671.     param1.nr_heads = param0.nr_heads = AUTO_HEADS;
  672.     param1.reduced_wr = param0.reduced_wr = AUTO_RWC;
  673.     param1.wr_precomp = param0.wr_precomp = AUTO_WPC;
  674.     param1.max_ecc = param0.max_ecc = AUTO_ECC;
  675.     param1.ctrl_byte = param0.ctrl_byte = AUTO_CTRL;
  676.     wini[DEV_PER_DRIVE].wn_heads = wini[0].wn_heads = param0.nr_heads;
  677.     wini[DEV_PER_DRIVE].wn_low = wini[0].wn_low = 0L;
  678.     wini[DEV_PER_DRIVE].wn_size = wini[0].wn_size = (long)AUTO_CYLS * (long)AUTO_HEADS * (long)NR_SECTORS;
  679.     if(w_reset() != OK)
  680.       panic("cannot setup for reading winchester parameter tables",0);
  681.  
  682.     if (nr_drives > 1) {
  683.       
  684.       /* generate the request to read the first sector from the winchester */
  685.       w_mess.DEVICE = DEV_PER_DRIVE;
  686.       w_mess.POSITION = 0L;
  687.       w_mess.COUNT = BLOCK_SIZE;
  688.       w_mess.ADDRESS = (char *) buf;
  689.       w_mess.PROC_NR = WINCHESTER;
  690.       w_mess.m_type = DISK_READ;
  691.       if(w_do_rdwt(&w_mess) != BLOCK_SIZE)
  692.         panic("cannot read drive parameters from winchester",DEV_PER_DRIVE);
  693.  
  694.       /* copy the parameter tables into the structures for later use */
  695.       copy_param(&buf[AUTO_PARAM], ¶m1);
  696.  
  697.     }
  698.  
  699.     /* generate the request to read the first sector from the winchester */
  700.     w_mess.DEVICE = 0;
  701.     w_mess.POSITION = 0L;
  702.     w_mess.COUNT = BLOCK_SIZE;
  703.     w_mess.ADDRESS = (char *) buf;
  704.     w_mess.PROC_NR = WINCHESTER;
  705.     w_mess.m_type = DISK_READ;
  706.     if(w_do_rdwt(&w_mess) != BLOCK_SIZE)
  707.       panic("cannot read drive parameters from winchester", 0);
  708.  
  709.     /* copy the parameter tables into the structures for later use */
  710.     copy_param(&buf[AUTO_PARAM], ¶m0);
  711.  
  712.        
  713.    /* whoever compiled the kernel wanted the auto bios code included. if it
  714.     * turns out that the tables should be read from the rom, then handle
  715.     * this case the regular way */
  716.   } else {
  717. #endif
  718.  
  719.   /* Calculate the drive types */
  720.   type_0 = i & 3;
  721.   type_1 = (i >> 2) & 3;
  722.  
  723.   /* Copy the parameter vector from the saved vector table */
  724.   offset = vec_table[2 * 0x41];
  725.   segment = vec_table[2 * 0x41 + 1];
  726.  
  727.   /* Calculate the address off the parameters and copy them to buf */
  728.   address = ((phys_bytes)segment << 4) + offset;
  729.   phys_copy(address, umap(proc_addr(WINCHESTER), D, buf, 64), 64L);
  730.  
  731.   /* Copy the parameters to the structures */
  732.   copy_param(&buf[type_0 * 16], ¶m0);
  733.   copy_param(&buf[type_1 * 16], ¶m1);
  734.  
  735. #if AUTO_BIOS
  736.   /* close up the code to be executed when the controller has not been
  737.    * set up to for auto configuration */
  738.   }
  739. #endif
  740.  
  741.   /* Set the parameters in the drive structure */
  742.   for (i = 0; i < DEV_PER_DRIVE; i++) {
  743.     wini[i].wn_heads = param0.nr_heads;
  744.     wini[i].wn_ctrl_byte = param0.ctrl_byte;
  745.     wini[i].wn_drive = 0 << 5;    /* Set drive number */
  746.   }
  747.  
  748.   wini[0].wn_low = wini[DEV_PER_DRIVE].wn_low = 0L;
  749.   wini[0].wn_size = (long)((long)param0.nr_cyl * (long)param0.nr_heads * (long)NR_SECTORS);
  750.  
  751.   for (i = DEV_PER_DRIVE; i < (2*DEV_PER_DRIVE); i++) {
  752.     wini[i].wn_heads = param1.nr_heads;
  753.     wini[i].wn_ctrl_byte = param1.ctrl_byte;
  754.     wini[i].wn_drive = 1 << 5;    /* Set drive number */
  755.   }
  756.   wini[DEV_PER_DRIVE].wn_size =
  757.     (long)((long)param1.nr_cyl * (long)param1.nr_heads * (long)NR_SECTORS);
  758.  
  759.   /* Initialize the controller */
  760.   if ((nr_drives > 0) && (w_reset() != OK))
  761.         nr_drives = 0;
  762.  
  763.   /* Read the partition table for each drive and save them */
  764.   for (i = 0; i < nr_drives; i++) {
  765.     w_mess.DEVICE = i * DEV_PER_DRIVE;
  766.     w_mess.POSITION = 0L;
  767.     w_mess.COUNT = BLOCK_SIZE;
  768.     w_mess.ADDRESS = (char *) buf;
  769.     w_mess.PROC_NR = WINCHESTER;
  770.     w_mess.m_type = DISK_READ;
  771.     if (w_do_rdwt(&w_mess) != BLOCK_SIZE)
  772.         panic("Can't read partition table of winchester ", i);
  773.     copy_prt(i * DEV_PER_DRIVE);
  774.   }
  775. }
  776.  
  777. /*==========================================================================*
  778.  *                                copy_params                                     *
  779.  *==========================================================================*/
  780. PRIVATE copy_params(src, dest)
  781. register unsigned char *src;
  782. register struct param *dest;
  783. {
  784. /* This routine copies the parameters from src to dest
  785.  * and sets the parameters for partition 0 and DEV_PER_DRIVE
  786. */
  787.  
  788.   dest->nr_cyl = *(int *)src;
  789.   dest->nr_heads = (int)src[2];
  790.   dest->reduced_wr = *(int *)&src[3];
  791.   dest->wr_precomp = *(int *)&src[5];
  792.   dest->max_ecc = (int)src[7];
  793.   dest->ctrl_byte = (int)src[8];
  794. }
  795.  
  796. /*==========================================================================*
  797.  *                                copy_prt                                    *
  798.  *==========================================================================*/
  799. PRIVATE copy_prt(drive)
  800. int drive;
  801. {
  802. /* This routine copies the partition table for the selected drive to
  803.  * the variables wn_low and wn_size
  804.  */
  805.  
  806.   register int i, offset;
  807.   struct wini *wn;
  808.   long adjust;
  809.  
  810.   for (i=0; i<4; i++) {
  811.     adjust = 0;
  812.     wn = &wini[i + drive + 1];
  813.     offset = PART_TABLE + i * 0x10;
  814.     wn->wn_low = *(long *)&buf[offset];
  815.     if ((wn->wn_low % (BLOCK_SIZE/SECTOR_SIZE)) != 0) {
  816.         adjust = wn->wn_low;
  817.         wn->wn_low = (wn->wn_low/(BLOCK_SIZE/SECTOR_SIZE)+1)*(BLOCK_SIZE/SECTOR_SIZE);
  818.         adjust = wn->wn_low - adjust;
  819.     }
  820.     wn->wn_size = *(long *)&buf[offset + sizeof(long)] - adjust;
  821.   }
  822.   sort(&wini[drive + 1]);
  823. }
  824.  
  825. sort(wn)
  826. register struct wini *wn;
  827. {
  828.   register int i,j;
  829.  
  830.   for (i=0; i<4; i++)
  831.     for (j=0; j<3; j++)
  832.         if ((wn[j].wn_low == 0) && (wn[j+1].wn_low != 0))
  833.             swap(&wn[j], &wn[j+1]);
  834.         else if (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)
  835.             swap(&wn[j], &wn[j+1]);
  836. }
  837.  
  838. swap(first, second)
  839. register struct wini *first, *second;
  840. {
  841.   register struct wini tmp;
  842.  
  843.   tmp = *first;
  844.   *first = *second;
  845.   *second = tmp;
  846. }
  847.  
  848.